`timescale 1ns / 1ps
//********************************************************************** 
// -------------------------------------------------------------------
// Disclaimers
// -------------------------------------------------------------------
// When you use this source file, please note that the author assumes 
// no legal or non-legal responsibility for any consequences of your use 
// of it including, but not limited to, the specific use of the code or 
// liability to you for infringement of any patent, copyright or other 
// intellectual property rights.
// If you do not agree to the terms, please do not use the file and 
// delete the file promptly.
// -------------------------------------------------------------------
// Copyright Notice
// -------------------------------------------------------------------
// This source file may be used for personal study, provided that this 
// copyright notice is not removed from the file.
// and that derivative works of this source file contain the original 
// copyright notice and related disclaimers.
// For commercial use, please contact the author for authorization, 
// otherwise the author reserves all rights.
// ------------------------------------------------------------------- 
// Author: Geeker_FPGA 
// Email:geeker_fpga@uisrc.com
// Date:2022/04
// Description: 
//  
// 
// Web:http://www.uisrc.com
//------------------------------------------------------------------- 
//*********************************************************************/
module asynch_fifo#(
	parameter FIFO_AFULL_SIZE   = 1,
	parameter FIFO_AEMPTY_SIZE  = 1,
	parameter FIFO_PTR   		= 4,     
	parameter FIFO_WIDTH 		= 8,
	parameter FIFO_MODE 		= "NORMAL"//"NORMAL" "SHOWAHEAD"
	// parameter FIFO_DEPTH = 16  
)(
	fifo_wrclk     ,
	fifo_wr_rst_n  ,
	fifo_wren      ,
	fifo_wrdata    ,
	fifo_rdclk     ,
	fifo_rd_rst_n  ,
	fifo_rden      ,
	fifo_rddata    ,
	fifo_full      ,
	fifo_empty     ,
	fifo_afull     ,
	fifo_aempty    ,
	fifo_room_avail,
	fifo_data_avail
);
input wire						fifo_wrclk     ;
input wire						fifo_wr_rst_n  ;
input wire						fifo_wren      ;
input wire	[FIFO_WIDTH-1:0] 	fifo_wrdata    ;
input wire						fifo_rdclk     ;
input wire						fifo_rd_rst_n  ;
input wire						fifo_rden      ;
output wire	[FIFO_WIDTH-1:0]	fifo_rddata    ;
output reg						fifo_full      ;
output reg						fifo_empty     ;
output wire						fifo_afull     ;
output wire						fifo_aempty    ;
output reg	[FIFO_PTR:0]		fifo_room_avail;
output reg	[FIFO_PTR:0]		fifo_data_avail;

localparam FIFO_DEPTH			  = (1<<FIFO_PTR);
localparam FIFO_TWICEDEPTH_MINUS1 = 2*FIFO_DEPTH - 1;

//****************REG**************************
reg     [FIFO_PTR:0]    wr_ptr_wab,wr_ptr_wab_nxt;//extra wraparound bit
wire 	[FIFO_PTR:0]	fifo_room_avail_nxt      ;
wire 					fifo_full_nxt            ;
wire 	[FIFO_PTR:0]    wr_ptr                   ;//write ptr without wraparound bit
reg     [FIFO_PTR:0]    rd_ptr_wab,rd_ptr_wab_nxt;//extra wraparound bit
wire    [FIFO_PTR:0]    fifo_data_avail_nxt      ;
wire 					fifo_empty_nxt           ;
wire 	[FIFO_PTR:0]    rd_ptr                   ;//read ptr without wraparound bit

reg 	[FIFO_PTR:0] 	wr_ptr_wab_gray          ;
wire 	[FIFO_PTR:0] 	wr_ptr_wab_gray_nxt      ;
reg 	[FIFO_PTR:0] 	wr_ptr_wab_gray_sync1    ;
reg 	[FIFO_PTR:0] 	wr_ptr_wab_gray_sync2    ;
reg 	[FIFO_PTR:0] 	wr_ptr_wab_rdclk         ;
wire 	[FIFO_PTR:0] 	wr_ptr_wab_rdclk_nxt     ;

reg 	[FIFO_PTR:0]	rd_ptr_wab_gray          ;
wire 	[FIFO_PTR:0]	rd_ptr_wab_gray_nxt      ;
reg 	[FIFO_PTR:0]	rd_ptr_wab_gray_sync1    ;
reg 	[FIFO_PTR:0]	rd_ptr_wab_gray_sync2    ;
reg 	[FIFO_PTR:0]	rd_ptr_wab_wrclk         ;
wire 	[FIFO_PTR:0]	rd_ptr_wab_wrclk_nxt     ;
//write pointer control logic
//*********************************************
always@(*)
begin
	wr_ptr_wab_nxt = wr_ptr_wab;
	if(fifo_wren&&(wr_ptr_wab == FIFO_TWICEDEPTH_MINUS1))
	begin
		wr_ptr_wab_nxt = 'd0;
	end	
	else if(fifo_wren)
	begin
		wr_ptr_wab_nxt = wr_ptr_wab + 1'b1;
	end	
end

always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
	if(!fifo_wr_rst_n)
	begin
		wr_ptr_wab            <= 'd0;
	end
	else
	begin
		wr_ptr_wab            <= wr_ptr_wab_nxt;
	end	
end

//convert the binary wr_ptr to gray,flop it,and then pass it to read domain
//*********************************************
binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_wr
(
	.binary_value	(wr_ptr_wab_nxt			),
	.gray_value		(wr_ptr_wab_gray_nxt	)
);

always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
	if(!fifo_wr_rst_n)
	begin
		wr_ptr_wab_gray       <= 'd0;
	end	
	else
	begin
		wr_ptr_wab_gray       <= wr_ptr_wab_gray_nxt;
	end	
end

//synchronize wr_ptr_wab_gray into read clock domain
//*********************************************
always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
	if(!fifo_rd_rst_n)
	begin
		wr_ptr_wab_gray_sync1       <= 'd0;
		wr_ptr_wab_gray_sync2       <= 'd0;
	end
	else
	begin
		wr_ptr_wab_gray_sync1       <= wr_ptr_wab_gray;
		wr_ptr_wab_gray_sync2       <= wr_ptr_wab_gray_sync1;
	end	
end


//convert wr_ptr_wab_gray_sync2 back to binary form
//*********************************************
gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_wr
(
	.gray_value		(wr_ptr_wab_gray_sync2	),
	.binary_value	(wr_ptr_wab_rdclk_nxt	)
);

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
	if(!fifo_rd_rst_n)
	begin
		wr_ptr_wab_rdclk       <= 'd0;
	end
	else
	begin
		wr_ptr_wab_rdclk       <= wr_ptr_wab_rdclk_nxt;
	end	
end

//read pointer control logic
//*********************************************
always@(*)
begin
	rd_ptr_wab_nxt = rd_ptr_wab;
    if(fifo_rden && (rd_ptr_wab == FIFO_TWICEDEPTH_MINUS1))
	begin
		rd_ptr_wab_nxt = 'd0;
	end
	else if(fifo_rden)
	begin
		rd_ptr_wab_nxt = rd_ptr_wab + 1'b1;
	end
end

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
	if(!fifo_rd_rst_n)
	begin
		rd_ptr_wab            <= 'd0;
	end
	else
	begin
		rd_ptr_wab            <= rd_ptr_wab_nxt;
	end	
end

//convert the binary rd_ptr to gray and then pass it to write clock domain 
//*********************************************
binary_to_gray#(.PTR(FIFO_PTR)) u_binary_to_gray_rd
(
	.binary_value	(rd_ptr_wab_nxt			),
	.gray_value		(rd_ptr_wab_gray_nxt	)
);

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
	if(!fifo_rd_rst_n)
	begin
		rd_ptr_wab_gray     	<= 'd0;
	end
	else
	begin
		rd_ptr_wab_gray      	<= rd_ptr_wab_gray_nxt;
	end	
end

//synchronize rd_ptr_wab_gray into write clock domain
//*********************************************
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
	if(!fifo_wr_rst_n)
	begin
		rd_ptr_wab_gray_sync1       <= 'd0;
		rd_ptr_wab_gray_sync2       <= 'd0;
	end
	else
	begin
		rd_ptr_wab_gray_sync1       <= rd_ptr_wab_gray;
		rd_ptr_wab_gray_sync2       <= rd_ptr_wab_gray_sync1;
	end	
end


//convert rd_ptr_wab_gray_sync2 back to binary form
//*********************************************
gray_to_binary#(.PTR(FIFO_PTR)) u_gray_to_binary_rd
(
	.gray_value		(rd_ptr_wab_gray_sync2	),
	.binary_value	(rd_ptr_wab_wrclk_nxt	)
);

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
	if(!fifo_rd_rst_n)
	begin
		rd_ptr_wab_wrclk       <= 'd0;
	end
	else
	begin
		rd_ptr_wab_wrclk       <= rd_ptr_wab_wrclk_nxt;
	end	
end

assign wr_ptr = wr_ptr_wab[FIFO_PTR-1:0];
assign rd_ptr = rd_ptr_wab[FIFO_PTR-1:0];

//the ram instantiation sdp
//*********************************************

sdp_ram #(      
	.DATA_W	 		 (FIFO_WIDTH   	 	), 
	.ADDR_WIDTH	 	 (FIFO_PTR          ),
	.REG_OUT	     (0                 )
)u_ram( 
	.data_in  		(fifo_wrdata	), 
	.wraddress		(wr_ptr			), 
	.wren  			(fifo_wren		), 
	.clk_w 			(fifo_wrclk		),
	.data_out  		(fifo_rddata	), 
	.rdaddress		(FIFO_MODE == "SHOWAHEAD" ? (rd_ptr + fifo_rden):rd_ptr), 
	.rden  			(FIFO_MODE == "SHOWAHEAD" ? 1'b1 :fifo_rden	),
	.clk_r 			(fifo_rdclk		), 
	.rst_n_w		(fifo_wr_rst_n  ),
	.rst_n_r		(fifo_rd_rst_n	)
);

//generate fifo_full:pointers equal,but the warp around bits are different
//*********************************************
assign fifo_full_nxt 		= 	(wr_ptr_wab_nxt[FIFO_PTR]     != rd_ptr_wab_wrclk_nxt[FIFO_PTR]) && 
								(wr_ptr_wab_nxt[FIFO_PTR-1:0] == rd_ptr_wab_wrclk_nxt[FIFO_PTR-1:0]);

assign fifo_room_avail_nxt	= 	(wr_ptr_wab[FIFO_PTR] == rd_ptr_wab_wrclk[FIFO_PTR])?
								(FIFO_DEPTH - (wr_ptr_wab[FIFO_PTR-1:0] - rd_ptr_wab_wrclk[FIFO_PTR-1:0])):
								(rd_ptr_wab_wrclk[FIFO_PTR-1:0] - wr_ptr_wab[FIFO_PTR-1:0]);

//generate fifo_empty:pointers are equal including the warp around bits 
//*********************************************
assign fifo_empty_nxt 		= 	(rd_ptr_wab_nxt[FIFO_PTR:0] == wr_ptr_wab_rdclk_nxt[FIFO_PTR:0]);

assign fifo_data_avail_nxt 	=	(rd_ptr_wab[FIFO_PTR] == wr_ptr_wab_rdclk[FIFO_PTR])?
								(wr_ptr_wab_rdclk[FIFO_PTR-1:0] - rd_ptr_wab[FIFO_PTR-1:0]):
                                (FIFO_DEPTH - (rd_ptr_wab[FIFO_PTR-1:0] - wr_ptr_wab_rdclk[FIFO_PTR-1:0]));

assign fifo_afull           = (fifo_room_avail_nxt <= FIFO_AFULL_SIZE)? 1:0;
assign fifo_aempty          = (fifo_data_avail_nxt <= FIFO_AEMPTY_SIZE ) ? 1:0;
                                
always@(posedge fifo_wrclk or negedge fifo_wr_rst_n)
begin
	if(!fifo_wr_rst_n)
	begin
		fifo_full             <= 1'b0;
		fifo_room_avail       <= 'd0;
	end	
	else
	begin
		fifo_full             <= fifo_full_nxt;
		fifo_room_avail       <= fifo_room_avail_nxt;
	end	
end

always@(posedge fifo_rdclk or negedge fifo_rd_rst_n)
begin
	if(!fifo_rd_rst_n)
	begin
		fifo_empty            <= 1'b1;
		fifo_data_avail       <= 'd0;
	end	
	else
	begin
		fifo_empty            <= fifo_empty_nxt;
		fifo_data_avail       <= fifo_data_avail_nxt;
	end	
end

endmodule

module binary_to_gray#(
	parameter PTR   = 4    //"1","0"	
)(
	binary_value   ,
	gray_value     
);

input	wire [PTR:0]	binary_value  ;
output	wire [PTR:0]	gray_value    ;

generate
	genvar i;
	for( i = 0 ; i < PTR ; i = i + 1 )
	begin
		assign gray_value[i] = binary_value[i] ^ binary_value[ i + 1 ];
	end
	
endgenerate

assign gray_value[PTR] = binary_value[PTR];

endmodule


module gray_to_binary#(
	parameter PTR   = 4    //"1","0"	
)(
	binary_value   ,
	gray_value     
);

input	wire [PTR:0]	gray_value    ;
output	wire [PTR:0]	binary_value  ;


generate
	genvar i;
	for( i = 0 ; i < PTR ; i = i + 1 )
	begin
		assign binary_value[i] = binary_value[i + 1] ^ gray_value[i];
	end
	
endgenerate

assign binary_value[PTR] = gray_value[PTR];

endmodule

module sdp_ram#(
	parameter DATA_W  	= 1,
	parameter ADDR_WIDTH= 9,
	parameter REG_OUT   = 1,
	parameter U_DLY     = 1
)(
	input  		[1				-1:0]	clk_w	    ,
	input  		[1				-1:0]	rst_n_w		,	
	input  		[1				-1:0]	wren	    ,
	input  		[ADDR_WIDTH		-1:0]	wraddress	,	
	input  		[DATA_W			-1:0]	data_in		,
	input  		[1				-1:0]	clk_r	    ,
	input  		[1				-1:0]	rst_n_r		,	
	input  		[1				-1:0]	rden		,		
	input  		[ADDR_WIDTH		-1:0]	rdaddress	,
	output      [DATA_W			-1:0]   data_out
);

/* localparam     ADDR_NUM  = 2**ADDR_WIDTH;

reg [DATA_W			-1:0] mem [ADDR_NUM			-1:0];
reg [DATA_W			-1:0] q_tmp 	;
reg [DATA_W			-1:0] q_tmp_1d 	;

always@(posedge clk_w or negedge rst_n_w)
begin
	if(!rst_n_w)
		mem[wraddress] <= #U_DLY 'd0;
	else if(wren==1'b1)
	begin
		mem[wraddress] <= #U_DLY data_in;
	end
end

always@(posedge clk_r or negedge rst_n_r)
begin
	if(!rst_n_r)
		q_tmp <= #U_DLY 'd0;
	else if(rden==1'b1)
	begin
		q_tmp <= #U_DLY mem[rdaddress];
	end
end

always@(posedge clk_r )
begin
	q_tmp_1d <= #U_DLY q_tmp;
end

assign data_out =(REG_OUT == 1) ? q_tmp_1d : q_tmp ; */


EG_LOGIC_BRAM #(
	.DATA_WIDTH_A ( DATA_W            ),
	.DATA_WIDTH_B ( DATA_W            ),
	.ADDR_WIDTH_A ( ADDR_WIDTH        ),
	.ADDR_WIDTH_B ( ADDR_WIDTH        ),
	.MODE         ( "PDPW"            ),// DP,SP,PDPW,FIFO
	.REGMODE_A    ( "NOREG"           ),// "NOREG", "OUTREG"
	.REGMODE_B    ( (REG_OUT == 1) ? "OUTREG":"NOREG" ),// "NOREG", "OUTREG"
	.WRITEMODE_A  ( "NORMAL"          ),// "NORMAL", "READBEFOREWRITE", "WRITETHROUGH"
	.WRITEMODE_B  ( "NORMAL"          ),// "NORMAL", "READBEFOREWRITE", "WRITETHROUGH"
	.RESETMODE    ( "ASYNC"           ),// "SYNC", "ASYNC"
	.INIT_FILE    ( "NONE"            ),
	.FILL_ALL     ( "NONE"            ),// "NONE" or some binary string like "0101", not wider than the wider port
	.IMPLEMENT    ( "9K"              ) // 9K | 9K(FAST) | 32K (all capitalized)
)u_bram(
	.dia  		(data_in			),
	.addra		(wraddress			), 
	.cea  		(wren				), 
	.clka 		(clk_w				), 
	.wea  		('d1				), 
	.rsta 		(!rst_n_w			), 
	.bea  		('d0				),
	.ocea 		('d0				), 
	.doa  		(					),
	
	.dib  		('d0				),
	.addrb		(rdaddress			), 
	.ceb  		(rden				), 
	.clkb 		(clk_r				), 
	.web  		('d0				), 
	.rstb 		(!rst_n_r			), 
	.beb  		('d0				),
	.oceb 		((REG_OUT == 1      ) ? 1'b1:1'b0), 
	.dob		(data_out			)
);


endmodule

